package com.neveu.android.ds.groupmessenger;

import java.util.Iterator;
import java.util.LinkedList;

import android.content.Context;

public class Messenger implements MessageListener {

	private NetServent servent;
	private int serverPort = 4321, clientPort = 1234;
	private Context mainContext;
	private LinkedList<Message> msgBacklog;
	private VectorClock myClock;
	private int myIndex;
	GroupMessengerActivity mainAct;

	public Messenger(GroupMessengerActivity mainAct)
	{
		mainContext = GroupMessengerActivity.getContext();
	    servent = new NetServent(mainContext.getResources().getString(R.string.loopback_ip), serverPort, clientPort);
        servent.setMessageListener(this);
		myClock = new VectorClock(servent.ports.length);
		myIndex = servent.getIndexByPid(servent.myEmulPort);
		msgBacklog = new LinkedList<Message>();
		this.mainAct = mainAct;
	}
	
	/*
	 * Pass in chat message from main activity. Assemble message object and send to NetServent.
	 */
	public void send(String s)
	{
		myClock.incrementClock(myIndex);
		Message m = new Message(servent.myEmulPort, myClock.getClock(), s);
		servent.sendMessage(m);
	}
	
	public void receiveMessage(Message m)
	{
		if(m.procID == servent.myEmulPort)
			deliver(m.message);
		else
		{
			msgBacklog.add(m);
			doCausal();
		}
		
	}
	
	private void deliver(String m)
	{
		mainAct.receiveMessage(m);
	}
	
	private void doCausal()
	{
		Iterator<Message> it = msgBacklog.iterator();
		
		while(it.hasNext())
		{
			Message m = it.next();
			
			int senderIndex = servent.getIndexByPid(m.procID);
			if(m.clock.get(senderIndex) != (myClock.getClock().get(senderIndex) + 1))
			{
				continue;
			}
			else
			{
				int i;
				for(i=0; i < m.clock.size(); i++)
				{
					if(i == senderIndex)
						continue;
					
					if(m.clock.get(i) > myClock.getClock().get(i))
						break;
				}
				
				if(i == m.clock.size())
				{
					deliver(m.message + "\t" + m.clock.toString());
					msgBacklog.remove();
					myClock.incrementClock(senderIndex);
				}
				
			}
		}
	}
	
	public void finish()
	{
		servent.disconnect();
	}
}
